//---------------------------------------------------------------------------

#include "vcl_stl.h"
#pragma hdrstop

#include "h_base.h"
#include "h_xml.h"
#include "h_global.h"
using namespace h_base;
using namespace h_xml;

#include "h_g.h"
#include "h_stageicon.h"
#include "h_stage.h"
#include "t_normaljobform.h"
#include "t_externjobform.h"
#include "t_eventfireform.h"
#include "t_eventwaitform.h"
#include "t_groupform.h"
#include "h_i18n.h"
#include "t_timeform.h"
#include "t_batchjobform.h"
#include "h_portal.h"
#include "t_mainform.h"

//---------------------------------------------------------------------------

static const char *JobStatusStringArr[] =
{
   "WAITING",
   "RUNNING",
   "SKIPPED",
   "FAIL",
   "SUCCESS",
   "UNKNOWN"
};

static const char *JobStatusToString(int Status)
{
   if ( Status < 0 || Status > 5 )
   {
      Status = 5;
   }

   return JobStatusStringArr[Status];
}

// class HStage

__fastcall HStage::HStage(void)
{
   FIcon = NULL;
   FLabel = NULL;
   FInDegreeWithinGroup = 0;
}

__fastcall HStage::~HStage(void)
{
   if( FIcon != NULL )
      delete FIcon;
}

UTF8String __fastcall HStage::GetGroupId(void)
{
   return FGroupId;
}

void __fastcall HStage::SetGroupId(const UTF8String &GroupId)
{
   FGroupId = GroupId;
}

UTF8String __fastcall HStage::GetName(void)
{
   return FName;
}

void __fastcall HStage::SetName(const UTF8String &Name)
{
   FName = Name;
}

HStageIcon * __fastcall HStage::GetIcon(void)
{
   return FIcon;
}

TLabel * __fastcall HStage::GetLabel()
{
   return NULL;
}

void __fastcall HStage::CreateIcon(TWinControl *Parent)
{
   int Width, Height;

   if( FIcon != NULL )
      return;

   FIcon = new HStageIcon(Parent);
   FIcon->SetType(GetIconType());
   FIcon->SetEnlarge(GetIconEnlarge());
   FIcon->SetCaption(GetCaption());
   GetIconSize(Width, Height);
   FIcon->Width = Width;
   FIcon->Height = Height;
   FIcon->Parent = Parent;
   FIcon->ShowHint = true;
   FIcon->Hint = GetHint();
}

void __fastcall HStage::Move(int x, int y)
{
   if( FIcon == NULL )
      return;

   FIcon->Left = x;
   FIcon->Top = y;
   AdjustLabel();
}

void __fastcall HStage::AdjustLabel()
{
   if ( FLabel == NULL )
   {
      return;
   }

   FLabel->Top = FIcon->Top + FIcon->Height + 4;
   FLabel->Left = FIcon->Left + FIcon->Width / 2 - FLabel->Width / 2;
}

void __fastcall HStage::RefreshCaption(void)
{
   int Width, Height;

   GetIconSize(Width, Height);
   FIcon->Width = Width;
   FIcon->Height = Height;
   FIcon->SetEnlarge(GetIconEnlarge());
   FIcon->SetCaption(GetCaption());
   FIcon->Hint = GetHint();

   if ( FLabel != NULL )
   {
      FLabel->Caption = GetCaption();
      AdjustLabel();
   }
}

int __fastcall HStage::StringToAlarmTimeType(const UTF8String &Str)
{
   if ( Str == "At the moment" || Str == "1" )
   {
      return 1;
   }

   if ( Str == "Duration from the batch starting" || Str == "2" )
   {
      return 2;
   }

   if ( Str == "Duration from the job starting" || Str == "3" )
   {
      return 3;
   }

   return 0;
}

UTF8String __fastcall HStage::AlarmTimeTypeToString(int Type)
{
   if ( Type == 1 )
   {
      return h_g::I18nString("h_stage:AT_THE_MOMENT");
   }

   if ( Type == 2 )
   {
      return h_g::I18nString("h_stage:DURATION_FROM_THE_BATCH_STARTING");
   }

   if ( Type == 3 )
   {
      return h_g::I18nString("h_stage:DURATION_FROM_THE_JOB_STARTING");
   }

   return "";
}

void __fastcall HStage::GetJobListInfo(HJobListInfo &Info)
{
   Info = HJobListInfo();
   Info.Name = GetGroupId() + h_g::JobNameDelimiter + GetName();
   Info.GroupId = GetGroupId();
   Info.Type = GetType();
   Info.SuccJobSet = FSuccJobSet;
   Info.InDegreeWithinGroup = FInDegreeWithinGroup;
}

// class HStageJob

__fastcall HStageJob::HStageJob(void)
   : HStage(), FInfo()
{
}

HStage * __fastcall HStageJob::Clone(void)
{
   return new HStageJob;
}

UTF8String __fastcall HStageJob::GetType(void)
{
   return "JOB";
}

int __fastcall HStageJob::GetIconEnlarge(void)
{
   return FInfo.Enlarge;
}

bool __fastcall HStageJob::EditProperty(bool PartFlag)
{
   return NormalJobForm->Edit(this, PartFlag);
}

void __fastcall HStageJob::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   FInfo.CalcFlag = false;

   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      FInfo.Command = Node->GetProperty("COMMAND").c_str();
      FInfo.RestoreCommand = Node->GetProperty("RESTORE_COMMAND").c_str();
      FInfo.SpecialDate = Node->GetProperty("SPECIAL_DATE").c_str();
      FInfo.BusinessType = Node->GetProperty("BUSINESS_TYPE").c_str();
      FInfo.CondNot = Node->GetProperty("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetProperty("CONDITION_FILE").c_str();
      FInfo.AdditionalLog = Node->GetProperty("ADDITIONAL_LOG").c_str();
      FInfo.AlarmTimeType = StringToAlarmTimeType(Node->GetProperty("ALARM_TIME_TYPE").c_str());
      FInfo.AlarmTime = Node->GetProperty("ALARM_TIME").c_str();
      FInfo.JobTrigger = Node->GetProperty("JOB_TRIGGER").c_str();
      FInfo.PartitionFile = Node->GetProperty("PARTITION_FILE").c_str();
      FInfo.Exclusive = Node->GetProperty("EXCLUSIVE") == "Y";
      FInfo.ParallelJobNumber = StrToInt(Node->GetProperty("PARALLEL_JOB_NUMBER").c_str());
      FInfo.Priority = StrToInt(Node->GetProperty("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetProperty("AUTO_RERUN").c_str();
      FInfo.Enlarge = StrToInt(Node->GetProperty("ENLARGE").c_str());
      FInfo.Anchor = Node->GetProperty("ANCHOR") == "Y";
      FInfo.Tag = Node->GetProperty("TAG").c_str();
      FInfo.RunningGroup = Node->GetProperty("RUNNING_GROUP").c_str();
      FInfo.LoopStartTime = Node->GetProperty("LOOP_START_TIME").c_str();
      FInfo.LoopEndTime = Node->GetProperty("LOOP_END_TIME").c_str();
      FInfo.LoopInterval = Node->GetProperty("LOOP_INTERVAL").c_str();
      FInfo.LoopCount = Node->GetProperty("LOOP_COUNT").c_str();
      FInfo.LoopConditionVariable = Node->GetProperty("LOOP_CONDITION_VARIABLE").c_str();
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.LastStartTime = Node->GetProperty("START_TIME").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = FInfo.LastStartTime;
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
      HString Value = Node->GetProperty("PART_FLAG");
      FInfo.PartFlag = Value == "Y" || Value == "true";
      Value = Node->GetProperty("BREAKPOINT");
      FInfo.BreakpointFlag = Value == "Y" || Value == "true";
   }
   else
   {
      FInfo.Command = Node->GetValue("COMMAND").c_str();
      FInfo.RestoreCommand = Node->GetValue("RESTORE_COMMAND").c_str();
      FInfo.SpecialDate = Node->GetValue("SPECIAL_DATE").c_str();
      FInfo.BusinessType = Node->GetValue("BUSINESS_TYPE").c_str();
      FInfo.CondNot = Node->GetValue("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetValue("CONDITION_FILE").c_str();
      FInfo.AdditionalLog = Node->GetValue("ADDITIONAL_LOG").c_str();
      FInfo.AlarmTimeType = StringToAlarmTimeType(Node->GetValue("ALARM_TIME_TYPE").c_str());
      FInfo.AlarmTime = Node->GetValue("ALARM_TIME").c_str();
      FInfo.JobTrigger = Node->GetValue("JOB_TRIGGER").c_str();
      FInfo.PartitionFile = Node->GetValue("PARTITION_FILE").c_str();
      FInfo.Exclusive = Node->GetValue("EXCLUSIVE") == "Y";
      FInfo.ParallelJobNumber = StrToInt(Node->GetValue("PARALLEL_JOB_NUMBER").c_str());
      FInfo.Priority = StrToInt(Node->GetValue("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetValue("AUTO_RERUN").c_str();
      FInfo.Enlarge = StrToInt(Node->GetValue("ENLARGE").c_str());
      FInfo.Anchor = Node->GetValue("ANCHOR") == "Y";
      FInfo.Tag = Node->GetValue("TAG").c_str();
      FInfo.RunningGroup = Node->GetValue("RUNNING_GROUP").c_str();
      FInfo.LoopStartTime = Node->GetValue("LOOP_START_TIME").c_str();
      FInfo.LoopEndTime = Node->GetValue("LOOP_END_TIME").c_str();
      FInfo.LoopInterval = Node->GetValue("LOOP_INTERVAL").c_str();
      FInfo.LoopCount = Node->GetValue("LOOP_COUNT").c_str();
      FInfo.LoopConditionVariable = Node->GetValue("LOOP_CONDITION_VARIABLE").c_str();
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.LastStartTime = Node->GetValue("START_TIME").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = FInfo.LastStartTime;
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
      HString Value = Node->GetValue("PART_FLAG");
      FInfo.PartFlag = Value == "Y" || Value == "true";
      Value = Node->GetValue("BREAKPOINT");
      FInfo.BreakpointFlag = Value == "Y" || Value == "true";
   }

   FInfo.Description = Node->GetValues("DESCRIPTION").c_str();
   FInfo.ResourceListStr = Node->GetValues("RESOURCE_LIST").c_str();

   {
      FInfo.ExternJobListStr = "";
      vector<string> StrVec;
      Node->GetValues("EXTERN_JOB_LIST", StrVec);
      for ( int k = 0; k < StrVec.size(); k++ )
      {
         const char *s = StrVec[k].c_str();
         UTF8String JobName;
         char w[500 + 1];
         int Pos = 0;
         Pos += ReadWord(s + Pos, w, " :", 500);
         ToUpper(w);
         UTF8String ExternGroup = w;
         Pos += ReadWord(s + Pos, w, " :", 500);
         if ( w[0] == ':' )
         {
            Pos += ReadWord(s + Pos, w, " :", 500);
            ToUpper(w);
            JobName = ExternGroup + h_g::JobNameDelimiter + w;
         }
         else
         {
            JobName = ExternGroup;
         }
         FInfo.ExternJobList.push_back(JobName);
         if ( k > 0 )
         {
            FInfo.ExternJobListStr = FInfo.ExternJobListStr + "\n";
         }
         FInfo.ExternJobListStr = FInfo.ExternJobListStr + JobName;
      }
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }

#ifdef SIMBAT_ENTERPRISE
   LoadProperty_aux(Node, XmlPropertyFlag, StatusStr);
#endif
}

void __fastcall HStageJob::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   FInfo.CalcFlag = false;

   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.LastStartTime = Node->GetProperty("START_TIME").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = FInfo.LastStartTime;
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
      HString Value = Node->GetProperty("PART_FLAG");
      FInfo.PartFlag = Value == "Y" || Value == "true";
      Value = Node->GetProperty("BREAKPOINT");
      FInfo.BreakpointFlag = Value == "Y" || Value == "true";
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.LastStartTime = Node->GetValue("START_TIME").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = FInfo.LastStartTime;
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
      HString Value = Node->GetValue("PART_FLAG");
      FInfo.PartFlag = Value == "Y" || Value == "true";
      Value = Node->GetValue("BREAKPOINT");
      FInfo.BreakpointFlag = Value == "Y" || Value == "true";
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }

   if ( FInfo.PartFlag )
   {
      vector<HXmlTree*> Nodes;
      Node->GetChildren("PART", Nodes);
      for ( int i = 0; i < Nodes.size(); i++ )
      {
         HXmlTree *PartXml = Nodes[i];
         HPartInfo Part;
         if ( XmlPropertyFlag )
         {
            Part.Id = PartXml->GetProperty("ID").c_str();
            if ( Part.Id == "" )
            {
               continue;
            }
            StatusStr = PartXml->GetPropertyUpper("STATUS");
            Part.RestoreStatus = PartXml->GetPropertyUpper("RESTORE_STATUS").c_str();
            Part.StartTime = PartXml->GetProperty("START_TIME").c_str();
            Part.EndTime = PartXml->GetProperty("END_TIME").c_str();
            Part.ServerId = StrToInt(PartXml->GetProperty("SERVER_ID").c_str());
            Part.Pid = StrToInt(PartXml->GetProperty("PID").c_str());
         }
         else
         {
            Part.Id = PartXml->GetValue("ID").c_str();
            if ( Part.Id == "" )
            {
               continue;
            }
            StatusStr = PartXml->GetValueUpper("STATUS");
            Part.RestoreStatus = PartXml->GetValueUpper("RESTORE_STATUS").c_str();
            Part.StartTime = PartXml->GetValue("START_TIME").c_str();
            Part.EndTime = PartXml->GetValue("END_TIME").c_str();
            Part.ServerId = StrToInt(PartXml->GetValue("SERVER_ID").c_str());
            Part.Pid = StrToInt(PartXml->GetValue("PID").c_str());
         }
         {
            const char *s = StatusStr.c_str();
            if ( isdigit(*s) )
            {
               Part.Status = JobStatusToString(StrToInt(s));
            }
            else
            {
               Part.Status = s;
            }
            Part.StatusInt = StatusToInt(Part.Status);
         }
         FInfo.PartMap[Part.Id] = Part;
      }
   }
}

UTF8String __fastcall HStageJob::GetCaption(void)
{
   return GetName();
}

int __fastcall HStageJob::GetIconType(void)
{
   return 0;
}

void __fastcall HStageJob::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapJobBg1[0]->Width * ( FInfo.Enlarge + 3 );
   Height = h_g::BitmapJobBg1[0]->Height;
}

UTF8String __fastcall HStageJob::GetHint()
{
   char TmpStr[8192 + 1];
   snprintf(TmpStr, 8192, h_g::I18nString("h_stage:JOB_HINT"),
      GetName().c_str(), FInfo.Command.c_str(), FInfo.Tag.c_str());
   UTF8String Hint = TmpStr;

   if( FInfo.Description != "" )
   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            Hint += UTF8String(h_g::I18nString("h_stage:DESCRIPTION")) +
               Text[i].c_str();
         else
            Hint += UTF8String("\n              ") + Text[i].c_str();
      }
   }

   return Hint;
}

void __fastcall HStageJob::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
      GetIcon()->SetColor(h_g::itRunning);
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
   GetIcon()->SetPartFlag(FInfo.PartitionFile != "");
   GetIcon()->SetExternFlag(!FInfo.ExternJobList.empty());
   GetIcon()->SetLoopFlag(FInfo.LoopInterval != "");
   GetIcon()->SetBreakpointFlag(FInfo.BreakpointFlag);
}

UTF8String __fastcall HStageJob::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageJob::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageJob::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);

   JobListInfo.Command = FInfo.Command;
   JobListInfo.CommandUpper = JobListInfo.Command.UpperCase();
   JobListInfo.Tag = FInfo.Tag;
   JobListInfo.ExternJobList = FInfo.ExternJobList;

   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            JobListInfo.Description = UTF8String(Text[i].c_str());
         else
            JobListInfo.Description += UTF8String(" ") + UTF8String(Text[i].c_str());
      }
   }

   JobListInfo.Status = FInfo.Status;
   JobListInfo.StartTime = FInfo.StartTime;
   JobListInfo.EndTime = FInfo.EndTime;
   JobListInfo.ElapsedTime = DiffTime(FInfo.StartTime, FInfo.EndTime, FInfo.IdleTimeSum);
   JobListInfo.IdleTimeSum = FInfo.IdleTimeSum;

   if ( FInfo.PartFlag )
   {
      int NumOfRunningPart = 0;
      int NumOfWaitingPart = 0;
      int NumOfSuccessPart = 0;
      int NumOfFailedPart = 0;
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         switch(Part.StatusInt)
         {
         case 0 :  // Unknown
            JobListInfo.PartUnknown = true;
            NumOfFailedPart++;
            break;
         case 1 :  // Failed
            JobListInfo.PartFail = true;
            NumOfFailedPart++;
            break;
         case 3 :  // Skipped
            JobListInfo.PartSkipped = true;
            NumOfFailedPart++;
            break;
         case 2 :  // Running
            JobListInfo.PartRunning = true;
            NumOfRunningPart++;
            break;
         case 4 :  // Success
            JobListInfo.PartSuccess = true;
            NumOfSuccessPart++;
            break;
         default : // Waiting;
            JobListInfo.PartWaiting = true;
            NumOfWaitingPart++;
         }
      }
      char TmpStr[200 + 1];
      snprintf(TmpStr, 200, "%5d/%5d/%5d/%5d/%5d", NumOfRunningPart,
         NumOfWaitingPart, NumOfSuccessPart, NumOfFailedPart,
         (int)FInfo.PartMap.size());
      JobListInfo.Partition = TmpStr;
      if ( NumOfFailedPart > 0 && ( JobListInfo.Status == "SUCCESS" || JobListInfo.Status == "RUNNING" ) )
      {
         snprintf(TmpStr, 200, "PFAIL(%d)", NumOfFailedPart);
         JobListInfo.Status = TmpStr;
      }
   }

   if ( FInfo.CalcFlag )
   {
      JobListInfo.TotalElapsed = FInfo.TotalElapsed;
      JobListInfo.Imbalanced = FInfo.Imbalanced;
      return;
   }

   if ( FInfo.PartFlag )
   {
      int TotalHour = 0;
      int TotalMin = 0;
      int TotalSec = 0;
      vector<int> TimeVec;
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         if ( Part.StartTime.TrimRight() == "" || Part.EndTime.TrimRight() == "" )
         {
            continue;
         }
         unsigned short Hour;
         unsigned short Min;
         unsigned short Sec;
         DiffTime(Part.StartTime, Part.EndTime, Hour, Min, Sec);
         TimeVec.push_back((int)Hour * 3600 + Min * 60 + Sec);
         TotalSec += Sec;
         if ( TotalSec >= 60 )
         {
            TotalMin += TotalSec / 60;
            TotalSec %= 60;
         }
         TotalMin += Min;
         if ( TotalMin >= 60 )
         {
            TotalHour += TotalMin / 60;
            TotalMin %= 60;
         }
         TotalHour += Hour;
      }
      if ( TotalHour >= 100 )
      {
         FInfo.TotalElapsed.sprintf("%5d:%02d:%02d", TotalHour,
            TotalMin, TotalSec);
      }
      else
      {
         FInfo.TotalElapsed.sprintf("   %02d:%02d:%02d", TotalHour,
            TotalMin, TotalSec);
      }
      sort(TimeVec.begin(), TimeVec.end());
      float Rate = 0;
      int Qty = TimeVec.size();
      if ( Qty >= 10 && TimeVec[Qty - 1] > 0 )
      {
         int n = Qty * 9 / 10;
         long Total = 0;
         for ( int i = 0; i < n; i++ )
         {
            Total += TimeVec[i];
         }
         Rate = (float)( TimeVec[Qty - 1] - (float)Total / n ) /
            TimeVec[Qty - 1] * 100;
      }
      if ( Rate < 0.001 )
      {
         FInfo.Imbalanced = "";
      }
      else
      {
         FInfo.Imbalanced.sprintf("%5.1f", Rate);
      }
   }
   else
   {
      FInfo.TotalElapsed = UTF8String("   ") + JobListInfo.ElapsedTime;
   }

   JobListInfo.TotalElapsed = FInfo.TotalElapsed;
   JobListInfo.Imbalanced = FInfo.Imbalanced;
   FInfo.CalcFlag = true;
}

void __fastcall HStageJob::GetPartTimeInfo(vector<HPartTimeInfo> &PartVec)
{
   UTF8String CurrentTime =
      TDateTime::CurrentDateTime().FormatString("yyyymmddhhnnss");
   UTF8String JobName = GetGroupId() + h_g::JobNameDelimiter + GetName();

   if ( FInfo.PartFlag )
   {
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         if ( Part.StartTime == "" ||
              ( Part.EndTime == "" && Part.Status != "RUNNING" ) )
         {
            continue;
         }
         HPartTimeInfo Info;
         Info.PartName = JobName + h_g::JobNameDelimiter + Part.Id;
         Info.StartTime = Part.StartTime;
         Info.EndTime = Part.EndTime == "" ? CurrentTime : Part.EndTime;
         PartVec.push_back(Info);
      }
   }
   else
   {
      if ( FInfo.StartTime == "" || FInfo.Status == "WAITING" )
      {
         return;
      }
      HPartTimeInfo Info;
      Info.PartName = JobName;
      Info.StartTime = FInfo.StartTime;
      Info.EndTime = FInfo.EndTime == "" ? CurrentTime : FInfo.EndTime;
      PartVec.push_back(Info);
   }
}

bool __fastcall HStageJob::GetGanttInfo(const TDateTime &StartTime,
   const TDateTime &EndTime, bool PartitionFlag, int Time, HGanttInfo &GanttInfo)
{
   TDateTime JobStartTime;
   TDateTime JobEndTime;

   if ( FInfo.LastStartTime == "" )
   {
      return false;
   }

   if ( !ToDateTime(FormatDateTime(FInfo.LastStartTime), JobStartTime) )
   {
      return false;
   }

   if ( JobStartTime >= EndTime )
   {
      return false;
   }

   if ( PartitionFlag && !FInfo.PartFlag )
   {
      return false;
   }

   if ( FInfo.EndTime != "" )
   {
      if ( !ToDateTime(FormatDateTime(FInfo.EndTime), JobEndTime) )
      {
         return false;
      }
      if ( JobEndTime <= StartTime )
      {
         return false;
      }
   }

   if ( Time > 0 )
   {
      unsigned short Hour;
      unsigned short Min;
      unsigned short Sec;
      DiffTime(JobStartTime, JobEndTime, Hour, Min, Sec);
      int TotalSec = Hour * 60 * 60 + Min * 60 + Sec;
      if ( TotalSec < Time )
      {
         return false;
      }
   }

   GanttInfo.Name = GetGroupId() + h_g::JobNameDelimiter + GetName();
   GanttInfo.Hint = GetHint();
   GanttInfo.Qty = 1;
   GanttInfo.ErrorRate = 0;

   if ( JobStartTime < StartTime )
   {
      GanttInfo.StartTime = StartTime;
      GanttInfo.StartFlag = true;
   }
   else
   {
      GanttInfo.StartTime = JobStartTime;
      GanttInfo.StartFlag = false;
   }

   if ( FInfo.EndTime == "" || JobEndTime > EndTime )
   {
      GanttInfo.EndTime = EndTime;
      GanttInfo.EndFlag = true;
   }
   else
   {
      GanttInfo.EndTime = JobEndTime;
      GanttInfo.EndFlag = false;
   }

   if ( FInfo.PartFlag )
   {
      vector<int> QtyVec;
      unsigned short Hour;
      unsigned short Min;
      unsigned short Sec;
      DiffTime(StartTime, EndTime, Hour, Min, Sec);
      int TotalSec = Hour * 60 * 60 + Min * 60 + Sec + 1;
      for ( int i = 0; i < TotalSec; i++ )
      {
         QtyVec.push_back(0);
      }
      GanttInfo.StartFlag = false;
      GanttInfo.EndFlag = false;
      int TotalPart = 0;
      int FailedPart = 0;
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         TDateTime JobStartTime;
         TDateTime JobEndTime;
         if ( Part.StartTime == "" )
         {
            continue;
         }
         if ( !ToDateTime(FormatDateTime(Part.StartTime), JobStartTime) )
         {
            continue;
         }
         if ( JobStartTime > EndTime )
         {
            continue;
         }
         if ( JobStartTime < StartTime )
         {
            JobStartTime = StartTime;
            GanttInfo.StartFlag = true;
         }
         if ( Part.EndTime == "" )
         {
            JobEndTime = EndTime;
            GanttInfo.EndFlag = true;
         }
         else
         {
            if ( !ToDateTime(FormatDateTime(Part.EndTime), JobEndTime) )
            {
               continue;
            }
            if ( JobEndTime < StartTime )
            {
               continue;
            }
            if ( JobEndTime > EndTime )
            {
               JobEndTime = EndTime;
               GanttInfo.EndFlag = true;
            }
         }
         TotalPart++;
         if ( Part.StatusInt == 0 || Part.StatusInt == 1 || Part.StatusInt == 3 )
         {
            FailedPart++;
         }
         DiffTime(StartTime, JobStartTime, Hour, Min, Sec);
         int PosStart = Hour * 60 * 60 + Min * 60 + Sec;
         DiffTime(StartTime, JobEndTime, Hour, Min, Sec);
         int PosEnd = Hour * 60 * 60 + Min * 60 + Sec;
         for ( int i = PosStart; i < PosEnd; i++ )
         {
            QtyVec[i]++;
         }
      }
      if ( TotalPart == 0 )
      {
         return false;
      }
      GanttInfo.ErrorRate = FailedPart * 100 / TotalPart;
      for ( int i = 0; i < QtyVec.size(); i++ )
      {
         if ( QtyVec[i] > GanttInfo.Qty )
         {
            GanttInfo.Qty = QtyVec[i];
         }
      }
      int PosStart = 0;
      int PosEnd = 0;
      for ( int i = 0; i < QtyVec.size(); i++ )
      {
         if ( QtyVec[i] > 0 )
         {
            if ( PosStart == 0 )
            {
               PosStart = i;
            }
            PosEnd = i;
         }
      }
      GanttInfo.StartTime = StartTime + SecToDateTime(PosStart);
      GanttInfo.EndTime = StartTime + SecToDateTime(PosEnd);
   }
   else
   {
      if ( FInfo.Status == "FAIL" || FInfo.Status == "SKIPPED" ||
           FInfo.Status == "UNKNOWN" )
      {
         GanttInfo.ErrorRate = 100;
      }
   }

   return GanttInfo.EndTime > GanttInfo.StartTime;
}

UTF8String __fastcall HStageJob::GetRestoreStatus(void)
{
   return FInfo.RestoreStatus;
}

void __fastcall HStageJob::SetRestoreStatus(const UTF8String &Status)
{
   FInfo.RestoreStatus = Status;
   SetColor();
}

bool __fastcall HStageJob::CanSkip()
{
   if ( FInfo.PartFlag )
   {
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         if ( Part.Status == "FAIL" || Part.Status == "UNKNOWN" )
         {
            return true;
         }
      }
      return false;
   }
   else
   {
      return ( FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN" );
   }
}

bool __fastcall HStageJob::CanRestore()
{
   if ( FInfo.PartFlag )
   {
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         if ( Part.RestoreStatus == "NEED_RESTORE" &&
              Part.Status != "SKIPPED" )
         {
            return true;
         }
      }
      return false;
   }
   else
   {
      return FInfo.RestoreStatus == "NEED_RESTORE";
   }
}

bool __fastcall HStageJob::CanRerun()
{
   if ( FInfo.PartFlag )
   {
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         if ( ( Part.Status == "FAIL" || Part.Status == "UNKNOWN" ) &&
              ( Part.RestoreStatus == "" || Part.RestoreStatus == "RESTORED" ) )
         {
            return true;
         }
      }
      return false;
   }
   else
   {
      return ( FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN" ) &&
         ( FInfo.RestoreStatus == "" || FInfo.RestoreStatus == "RESTORED" );
   }
}

bool __fastcall HStageJob::CanRestart()
{
   return FInfo.Status == "SUCCESS" || FInfo.Status == "SKIPPED";
}

bool __fastcall HStageJob::CanStop()
{
   if ( FInfo.PartFlag )
   {
      for ( map<UTF8String, HPartInfo>::iterator Iter = FInfo.PartMap.begin();
            Iter != FInfo.PartMap.end(); Iter++ )
      {
         HPartInfo &Part = Iter->second;
         if ( Part.Status == "RUNNING" )
         {
            return true;
         }
      }
      return false;
   }
   else
   {
      return FInfo.Status == "RUNNING";
   }
}

bool __fastcall HStageJob::CanViewLog()
{
   return FInfo.StartTime != "";
}

bool __fastcall HStageJob::CanViewAdditionalLog()
{
   return FInfo.StartTime != "" && ( FInfo.AdditionalLog != "" || FInfo.PartFlag );
}

bool __fastcall HStageJob::CanViewRestoreLog()
{
   return FInfo.StartTime != "";
}

bool __fastcall HStageJob::CanBreakpoint()
{
   return FInfo.Status == "WAITING" ||
          ( FInfo.PartFlag &&
            ( FInfo.Status == "FAIL" || FInfo.Status == "RUNNING" ) );
}

void __fastcall HStageJob::GetInfo(HInfo &Info)
{
   FInfo.Name = GetName();
   Info = FInfo;
}

bool __fastcall HStageJob::CanChangeCondition()
{
   return true;
}

void __fastcall HStageJob::GetJobCondition(bool &CondNot,
   UTF8String &ConditionFile, UTF8String &SpecialDate,
   UTF8String &LoopStartTime, UTF8String &LoopEndTime)
{
   CondNot = FInfo.CondNot;
   ConditionFile = FInfo.ConditionFile;
   SpecialDate = FInfo.SpecialDate;
   LoopStartTime = FInfo.LoopStartTime;
   LoopEndTime = FInfo.LoopEndTime;
}

void __fastcall HStageJob::SetJobCondition(bool CondNot,
   const UTF8String &ConditionFile, const UTF8String &SpecialDate,
   const UTF8String &LoopStartTime, const UTF8String &LoopEndTime)
{
   FInfo.CondNot = CondNot;
   FInfo.ConditionFile = ConditionFile;
   FInfo.SpecialDate = SpecialDate;
   FInfo.LoopStartTime = LoopStartTime;
   FInfo.LoopEndTime = LoopEndTime;
}

// class HStageConnector

HStage * __fastcall HStageConnector::Clone(void)
{
   return new HStageConnector;
}

UTF8String __fastcall HStageConnector::GetType(void)
{
   return "CONNECTOR";
}

int __fastcall HStageConnector::GetIconEnlarge(void)
{
   return 0;
}

bool __fastcall HStageConnector::EditProperty(bool PartFlag)
{
   return false;
}

void __fastcall HStageConnector::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   if ( XmlPropertyFlag )
   {
      HString Value = Node->GetProperty("HIDE_ARROW");
      FInfo.HideArrow = Value == "Y" || Value == "true";
   }
   else
   {
      HString Value = Node->GetValue("HIDE_ARROW");
      FInfo.HideArrow = Value == "Y" || Value == "true";
   }
}

void __fastcall HStageConnector::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
}

UTF8String __fastcall HStageConnector::GetCaption(void)
{
   return "";
}

int __fastcall HStageConnector::GetIconType(void)
{
   return 1;
}

void __fastcall HStageConnector::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapConnector[0]->Width;
   Height = h_g::BitmapConnector[0]->Height;
}

UTF8String __fastcall HStageConnector::GetHint()
{
   char TmpStr[8192 + 1];
   snprintf(TmpStr, 8192, h_g::I18nString("h_stage:CONNECTOR_HINT"),
      ( FInfo.HideArrow ? "true" : "false" ));
   UTF8String Hint = TmpStr;
   return Hint;
}

void __fastcall HStageConnector::GetInfo(HInfo &Info)
{
   Info = FInfo;
}

void __fastcall HStageConnector::SetColor(void)
{
   GetIcon()->SetColor(h_g::itNormal);
}

UTF8String __fastcall HStageConnector::GetStatus(void)
{
   return "";
}

void __fastcall HStageConnector::SetStatus(const UTF8String &Status)
{
}

void __fastcall HStageConnector::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);
}

// class HStageExternJob

HStage * __fastcall HStageExternJob::Clone(void)
{
   return new HStageExternJob;
}

UTF8String __fastcall HStageExternJob::GetType(void)
{
   return "EXTERN_JOB";
}

int __fastcall HStageExternJob::GetIconEnlarge(void)
{
   return FInfo.Enlarge;
}

bool __fastcall HStageExternJob::EditProperty(bool PartFlag)
{
   return ExternJobForm->Edit(this);
}

void __fastcall HStageExternJob::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      FInfo.ExternGroup = Node->GetProperty("EXTERN_GROUP").c_str();
      FInfo.ExternJob = Node->GetProperty("EXTERN_JOB").c_str();
      FInfo.Description = Node->GetValues("DESCRIPTION").c_str();
      FInfo.Enlarge = StrToInt(Node->GetProperty("ENLARGE").c_str());
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
   }
   else
   {
      FInfo.ExternGroup = Node->GetValue("EXTERN_GROUP").c_str();
      FInfo.ExternJob = Node->GetValue("EXTERN_JOB").c_str();
      FInfo.Description = Node->GetValues("DESCRIPTION").c_str();
      FInfo.Enlarge = StrToInt(Node->GetValue("ENLARGE").c_str());
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

void __fastcall HStageExternJob::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

UTF8String __fastcall HStageExternJob::GetCaption(void)
{
   return FInfo.ExternGroup + "." + FInfo.ExternJob;
}

int __fastcall HStageExternJob::GetIconType(void)
{
   return 2;
}

void __fastcall HStageExternJob::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapExternJobBg1[0]->Width * ( FInfo.Enlarge + 3 );
   Height = h_g::BitmapExternJobBg1[0]->Height;
}

UTF8String __fastcall HStageExternJob::GetHint()
{
   char TmpStr[8192 + 1];
   snprintf(TmpStr, 8192, h_g::I18nString("h_stage:EXTERN_JOB_HINT"),
      FInfo.ExternGroup.c_str(), FInfo.ExternJob.c_str());
   UTF8String Hint = TmpStr;

   if( FInfo.Description != "" )
   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            Hint += UTF8String(h_g::I18nString("h_stage:DESCRIPTION")) +
               Text[i].c_str();
         else
            Hint += UTF8String("\n              ") + Text[i].c_str();
      }
   }

   return Hint;
}

void __fastcall HStageExternJob::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
      GetIcon()->SetColor(h_g::itRunning);
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
}

UTF8String __fastcall HStageExternJob::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageExternJob::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageExternJob::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);

   JobListInfo.ExternJob = FInfo.ExternGroup + h_g::JobNameDelimiter +
      FInfo.ExternJob;
}

void __fastcall HStageExternJob::GetInfo(HInfo &Info)
{
   Info = FInfo;
}

// class HStageEventFire

HStage * __fastcall HStageEventFire::Clone(void)
{
   return new HStageEventFire;
}

UTF8String __fastcall HStageEventFire::GetType(void)
{
   return "EVENT_FIRE";
}

int __fastcall HStageEventFire::GetIconEnlarge(void)
{
   return 0;
}

bool __fastcall HStageEventFire::EditProperty(bool PartFlag)
{
   return EventFireForm->Edit(this);
}

void __fastcall HStageEventFire::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      FInfo.HostName = Node->GetProperty("HOST_NAME").c_str();
      FInfo.Event = Node->GetPropertyUpper("EVENT").c_str();
      FInfo.SpecialDate = Node->GetProperty("SPECIAL_DATE").c_str();
      FInfo.CondNot = Node->GetProperty("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetProperty("CONDITION_FILE").c_str();
      FInfo.JobTrigger = Node->GetProperty("JOB_TRIGGER").c_str();
      FInfo.Priority = StrToInt(Node->GetProperty("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetProperty("AUTO_RERUN").c_str();
      FInfo.Tag = Node->GetProperty("TAG").c_str();
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      FInfo.HostName = Node->GetValue("HOST_NAME").c_str();
      FInfo.Event = Node->GetValueUpper("EVENT").c_str();
      FInfo.SpecialDate = Node->GetValue("SPECIAL_DATE").c_str();
      FInfo.CondNot = Node->GetValue("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetValue("CONDITION_FILE").c_str();
      FInfo.JobTrigger = Node->GetValue("JOB_TRIGGER").c_str();
      FInfo.Priority = StrToInt(Node->GetValue("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetValue("AUTO_RERUN").c_str();
      FInfo.Tag = Node->GetValue("TAG").c_str();
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   FInfo.Description = Node->GetValues("DESCRIPTION").c_str();

   {
      FInfo.ExternJobListStr = "";
      vector<string> StrVec;
      Node->GetValues("EXTERN_JOB_LIST", StrVec);
      for ( int k = 0; k < StrVec.size(); k++ )
      {
         const char *s = StrVec[k].c_str();
         UTF8String JobName;
         char w[500 + 1];
         int Pos = 0;
         Pos += ReadWord(s + Pos, w, " :", 500);
         ToUpper(w);
         UTF8String ExternGroup = w;
         Pos += ReadWord(s + Pos, w, " :", 500);
         if ( w[0] == ':' )
         {
            Pos += ReadWord(s + Pos, w, " :", 500);
            ToUpper(w);
            JobName = ExternGroup + h_g::JobNameDelimiter + w;
         }
         else
         {
            JobName = ExternGroup;
         }
         FInfo.ExternJobList.push_back(JobName);
         if ( k > 0 )
         {
            FInfo.ExternJobListStr = FInfo.ExternJobListStr + "\n";
         }
         FInfo.ExternJobListStr = FInfo.ExternJobListStr + JobName;
      }
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

void __fastcall HStageEventFire::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

UTF8String __fastcall HStageEventFire::GetCaption(void)
{
   return "";
}

int __fastcall HStageEventFire::GetIconType(void)
{
   return 3;
}

void __fastcall HStageEventFire::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapFwBg[0]->Width;
   Height = h_g::BitmapFwBg[0]->Height;
}

UTF8String __fastcall HStageEventFire::GetHint()
{
   char TmpStr[8192 + 1];
   snprintf(TmpStr, 8192, h_g::I18nString("h_stage:EVENT_FIRE_HINT"),
      FInfo.HostName.c_str(), FInfo.Event.c_str());
   UTF8String Hint = TmpStr;

   if( FInfo.Description != "" )
   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            Hint += UTF8String(h_g::I18nString("h_stage:DESCRIPTION")) +
               Text[i].c_str();
         else
            Hint += UTF8String("\n              ") + Text[i].c_str();
      }
   }

   return Hint;
}

void __fastcall HStageEventFire::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
      GetIcon()->SetColor(h_g::itRunning);
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
   GetIcon()->SetPartFlag(false);
   GetIcon()->SetExternFlag(!FInfo.ExternJobList.empty());
}

UTF8String __fastcall HStageEventFire::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageEventFire::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageEventFire::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);

   JobListInfo.Command = UTF8String("simbat_evnt fire \"") +
      FInfo.HostName + "\" \"" +
      FInfo.Event + "\"";
   JobListInfo.CommandUpper = JobListInfo.Command.UpperCase();
   JobListInfo.Tag = FInfo.Tag;
   JobListInfo.ExternJobList = FInfo.ExternJobList;

   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            JobListInfo.Description = UTF8String(Text[i].c_str());
         else
            JobListInfo.Description += UTF8String(" ") + UTF8String(Text[i].c_str());
      }
   }

   JobListInfo.Status = FInfo.Status;
   JobListInfo.StartTime = FInfo.StartTime;
   JobListInfo.EndTime = FInfo.EndTime;
   JobListInfo.ElapsedTime = DiffTime(FInfo.StartTime, FInfo.EndTime, FInfo.IdleTimeSum);
   JobListInfo.IdleTimeSum = FInfo.IdleTimeSum;
   JobListInfo.TotalElapsed = JobListInfo.ElapsedTime;
}

void __fastcall HStageEventFire::GetPartTimeInfo(vector<HPartTimeInfo> &PartVec)
{
   UTF8String CurrentTime =
      TDateTime::CurrentDateTime().FormatString("yyyymmddhhnnss");
   UTF8String JobName = GetGroupId() + h_g::JobNameDelimiter + GetName();

   if ( FInfo.StartTime == "" ||
        ( FInfo.EndTime == "" && FInfo.Status != "RUNNING" ) )
   {
      return;
   }

   HPartTimeInfo Info;
   Info.PartName = JobName;
   Info.StartTime = FInfo.StartTime;
   Info.EndTime = FInfo.EndTime == "" ? CurrentTime : FInfo.EndTime;
   PartVec.push_back(Info);
}

UTF8String __fastcall HStageEventFire::GetRestoreStatus(void)
{
   return FInfo.RestoreStatus;
}

bool __fastcall HStageEventFire::CanSkip()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageEventFire::CanRestore()
{
   return false;
}

bool __fastcall HStageEventFire::CanRerun()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageEventFire::CanRestart()
{
   return FInfo.Status == "SUCCESS" || FInfo.Status == "SKIPPED";
}

bool __fastcall HStageEventFire::CanStop()
{
   return FInfo.Status == "RUNNING";
}

bool __fastcall HStageEventFire::CanViewLog()
{
   return FInfo.StartTime != "";
}

bool __fastcall HStageEventFire::CanViewAdditionalLog()
{
   return false;
}

void __fastcall HStageEventFire::GetInfo(HInfo &Info)
{
   FInfo.Name = GetName();
   Info = FInfo;
}

bool __fastcall HStageEventFire::CanChangeCondition()
{
   return true;
}

void __fastcall HStageEventFire::GetJobCondition(bool &CondNot,
   UTF8String &ConditionFile, UTF8String &SpecialDate,
   UTF8String &LoopStartTime, UTF8String &LoopEndTime)
{
   CondNot = FInfo.CondNot;
   ConditionFile = FInfo.ConditionFile;
   SpecialDate = FInfo.SpecialDate;
   LoopStartTime = "";
   LoopEndTime = "";
}

void __fastcall HStageEventFire::SetJobCondition(bool CondNot,
   const UTF8String &ConditionFile, const UTF8String &SpecialDate,
   const UTF8String &LoopStartTime, const UTF8String &LoopEndTime)
{
   FInfo.CondNot = CondNot;
   FInfo.ConditionFile = ConditionFile;
   FInfo.SpecialDate = SpecialDate;
}

// class HStageEventWait

HStage * __fastcall HStageEventWait::Clone(void)
{
   return new HStageEventWait;
}

UTF8String __fastcall HStageEventWait::GetType(void)
{
   return "EVENT_WAIT";
}

int __fastcall HStageEventWait::GetIconEnlarge(void)
{
   return 0;
}

bool __fastcall HStageEventWait::EditProperty(bool PartFlag)
{
   return EventWaitForm->Edit(this);
}

void __fastcall HStageEventWait::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      FInfo.Event = Node->GetPropertyUpper("EVENT").c_str();
      FInfo.WaitForFile = Node->GetProperty("WAIT_FOR_FILE").c_str();
      FInfo.WaitForTime = Node->GetProperty("WAIT_FOR_TIME").c_str();
      FInfo.WaitForCommand = Node->GetProperty("WAIT_FOR_COMMAND").c_str();
      FInfo.WaitForOperator = Node->GetProperty("WAIT_FOR_OPERATOR") == "Y";
      FInfo.SpecialDate = Node->GetProperty("SPECIAL_DATE").c_str();
      FInfo.CondNot = Node->GetProperty("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetProperty("CONDITION_FILE").c_str();
      FInfo.JobTrigger = Node->GetProperty("JOB_TRIGGER").c_str();
      FInfo.Priority = StrToInt(Node->GetProperty("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetProperty("AUTO_RERUN").c_str();
      FInfo.Tag = Node->GetProperty("TAG").c_str();
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      FInfo.Event = Node->GetValueUpper("EVENT").c_str();
      FInfo.WaitForFile = Node->GetValue("WAIT_FOR_FILE").c_str();
      FInfo.WaitForTime = Node->GetValue("WAIT_FOR_TIME").c_str();
      FInfo.WaitForCommand = Node->GetValue("WAIT_FOR_COMMAND").c_str();
      FInfo.WaitForOperator = Node->GetValue("WAIT_FOR_OPERATOR") == "Y";
      FInfo.SpecialDate = Node->GetValue("SPECIAL_DATE").c_str();
      FInfo.CondNot = Node->GetValue("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetValue("CONDITION_FILE").c_str();
      FInfo.JobTrigger = Node->GetValue("JOB_TRIGGER").c_str();
      FInfo.Priority = StrToInt(Node->GetValue("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetValue("AUTO_RERUN").c_str();
      FInfo.Tag = Node->GetValue("TAG").c_str();
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   FInfo.Description = Node->GetValues("DESCRIPTION").c_str();

   {
      FInfo.ExternJobListStr = "";
      vector<string> StrVec;
      Node->GetValues("EXTERN_JOB_LIST", StrVec);
      for ( int k = 0; k < StrVec.size(); k++ )
      {
         const char *s = StrVec[k].c_str();
         UTF8String JobName;
         char w[500 + 1];
         int Pos = 0;
         Pos += ReadWord(s + Pos, w, " :", 500);
         ToUpper(w);
         UTF8String ExternGroup = w;
         Pos += ReadWord(s + Pos, w, " :", 500);
         if ( w[0] == ':' )
         {
            Pos += ReadWord(s + Pos, w, " :", 500);
            ToUpper(w);
            JobName = ExternGroup + h_g::JobNameDelimiter + w;
         }
         else
         {
            JobName = ExternGroup;
         }
         FInfo.ExternJobList.push_back(JobName);
         if ( k > 0 )
         {
            FInfo.ExternJobListStr = FInfo.ExternJobListStr + "\n";
         }
         FInfo.ExternJobListStr = FInfo.ExternJobListStr + JobName;
      }
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

void __fastcall HStageEventWait::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

UTF8String __fastcall HStageEventWait::GetCaption(void)
{
   return "";
}

int __fastcall HStageEventWait::GetIconType(void)
{
   return 4;
}

void __fastcall HStageEventWait::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapFwBg[0]->Width;
   Height = h_g::BitmapFwBg[0]->Height;
}

UTF8String __fastcall HStageEventWait::GetHint()
{
   UTF8String Hint = h_g::I18nString("h_stage:EVENT_WAIT_HINT");

   if ( FInfo.Event != "" )
   {
      Hint += UTF8String(h_g::I18nString("h_stage:EVENT")) + FInfo.Event;
   }

   if ( FInfo.WaitForFile != "" )
   {
      Hint += UTF8String(h_g::I18nString("h_stage:WAIT_FILE")) + FInfo.WaitForFile;
   }

   if ( FInfo.WaitForTime != "" )
   {
      Hint += UTF8String(h_g::I18nString("h_stage:WAIT_TIME")) + FInfo.WaitForTime;
   }

   if ( FInfo.WaitForCommand != "" )
   {
      Hint += UTF8String(h_g::I18nString("h_stage:WAIT_COMMAND")) + FInfo.WaitForCommand;
   }

   if( FInfo.Description != "" )
   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            Hint += UTF8String(h_g::I18nString("h_stage:DESCRIPTION")) +
               Text[i].c_str();
         else
            Hint += UTF8String("\n              ") + Text[i].c_str();
      }
   }

   return Hint;
}

void __fastcall HStageEventWait::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
      GetIcon()->SetColor(h_g::itRunning);
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
   GetIcon()->SetPartFlag(false);
   GetIcon()->SetExternFlag(!FInfo.ExternJobList.empty());
}

UTF8String __fastcall HStageEventWait::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageEventWait::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageEventWait::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);

   JobListInfo.Command = UTF8String("simbat_evnt wait \"") +
      FInfo.Event + "\" \"" +
      FInfo.WaitForFile + "\" \"" +
      FInfo.WaitForTime + "\" '" +
      FInfo.WaitForCommand + "'";
   JobListInfo.CommandUpper = JobListInfo.Command.UpperCase();
   JobListInfo.Tag = FInfo.Tag;
   JobListInfo.ExternJobList = FInfo.ExternJobList;

   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            JobListInfo.Description = UTF8String(Text[i].c_str());
         else
            JobListInfo.Description += UTF8String(" ") + UTF8String(Text[i].c_str());
      }
   }

   JobListInfo.Status = FInfo.Status;
   JobListInfo.StartTime = FInfo.StartTime;
   JobListInfo.EndTime = FInfo.EndTime;
   JobListInfo.ElapsedTime = DiffTime(FInfo.StartTime, FInfo.EndTime, FInfo.IdleTimeSum);
   JobListInfo.IdleTimeSum = FInfo.IdleTimeSum;
   JobListInfo.TotalElapsed = JobListInfo.ElapsedTime;
}

void __fastcall HStageEventWait::GetPartTimeInfo(vector<HPartTimeInfo> &PartVec)
{
   UTF8String CurrentTime =
      TDateTime::CurrentDateTime().FormatString("yyyymmddhhnnss");
   UTF8String JobName = GetGroupId() + h_g::JobNameDelimiter + GetName();

   if ( FInfo.StartTime == "" ||
        ( FInfo.EndTime == "" && FInfo.Status != "RUNNING" ) )
   {
      return;
   }

   HPartTimeInfo Info;
   Info.PartName = JobName;
   Info.StartTime = FInfo.StartTime;
   Info.EndTime = FInfo.EndTime == "" ? CurrentTime : FInfo.EndTime;
   PartVec.push_back(Info);
}

UTF8String __fastcall HStageEventWait::GetRestoreStatus(void)
{
   return FInfo.RestoreStatus;
}

bool __fastcall HStageEventWait::CanSkip()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageEventWait::CanRestore()
{
   return false;
}

bool __fastcall HStageEventWait::CanRerun()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageEventWait::CanRestart()
{
   return FInfo.Status == "SUCCESS" || FInfo.Status == "SKIPPED";
}

bool __fastcall HStageEventWait::CanStop()
{
   return FInfo.Status == "RUNNING";
}

bool __fastcall HStageEventWait::CanViewLog()
{
   return FInfo.StartTime != "";
}

bool __fastcall HStageEventWait::CanViewAdditionalLog()
{
   return false;
}

void __fastcall HStageEventWait::GetInfo(HInfo &Info)
{
   FInfo.Name = GetName();
   Info = FInfo;
}

bool __fastcall HStageEventWait::CanChangeCondition()
{
   return true;
}

void __fastcall HStageEventWait::GetJobCondition(bool &CondNot,
   UTF8String &ConditionFile, UTF8String &SpecialDate,
   UTF8String &LoopStartTime, UTF8String &LoopEndTime)
{
   CondNot = FInfo.CondNot;
   ConditionFile = FInfo.ConditionFile;
   SpecialDate = FInfo.SpecialDate;
   LoopStartTime = "";
   LoopEndTime = "";
}

void __fastcall HStageEventWait::SetJobCondition(bool CondNot,
   const UTF8String &ConditionFile, const UTF8String &SpecialDate,
   const UTF8String &LoopStartTime, const UTF8String &LoopEndTime)
{
   FInfo.CondNot = CondNot;
   FInfo.ConditionFile = ConditionFile;
   FInfo.SpecialDate = SpecialDate;
}

// class HStageGroup

HStage * __fastcall HStageGroup::Clone(void)
{
   return new HStageGroup;
}

UTF8String __fastcall HStageGroup::GetType(void)
{
   return "GROUP";
}

int __fastcall HStageGroup::GetIconEnlarge(void)
{
   return FInfo.Enlarge;
}

bool __fastcall HStageGroup::EditProperty(bool PartFlag)
{
   return GroupForm->Edit(this);
}

void __fastcall HStageGroup::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      FInfo.Group = Node->GetPropertyUpper("GROUP").c_str();
      FInfo.SpecialDate = Node->GetProperty("SPECIAL_DATE").c_str();
      FInfo.BusinessType = Node->GetProperty("BUSINESS_TYPE").c_str();
      FInfo.CondNot = Node->GetProperty("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetProperty("CONDITION_FILE").c_str();
      FInfo.ParallelJobNumber = StrToInt(Node->GetProperty("PARALLEL_JOB_NUMBER").c_str());
      FInfo.Priority = StrToInt(Node->GetProperty("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetProperty("AUTO_RERUN").c_str();
      FInfo.Enlarge = StrToInt(Node->GetProperty("ENLARGE").c_str());
      FInfo.Tag = Node->GetProperty("TAG").c_str();
      FInfo.RunningGroup = Node->GetProperty("RUNNING_GROUP").c_str();
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
   }
   else
   {
      FInfo.Group = Node->GetValueUpper("GROUP").c_str();
      FInfo.SpecialDate = Node->GetValue("SPECIAL_DATE").c_str();
      FInfo.BusinessType = Node->GetValue("BUSINESS_TYPE").c_str();
      FInfo.CondNot = Node->GetValue("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetValue("CONDITION_FILE").c_str();
      FInfo.ParallelJobNumber = StrToInt(Node->GetValue("PARALLEL_JOB_NUMBER").c_str());
      FInfo.Priority = StrToInt(Node->GetValue("PRIORITY").c_str());
      FInfo.AutoRerun = Node->GetValue("AUTO_RERUN").c_str();
      FInfo.Enlarge = StrToInt(Node->GetValue("ENLARGE").c_str());
      FInfo.Tag = Node->GetValue("TAG").c_str();
      FInfo.RunningGroup = Node->GetValue("RUNNING_GROUP").c_str();
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
   }

   FInfo.Description = Node->GetValues("DESCRIPTION").c_str();

   {
      FInfo.ExternJobListStr = "";
      vector<string> StrVec;
      Node->GetValues("EXTERN_JOB_LIST", StrVec);
      for ( int k = 0; k < StrVec.size(); k++ )
      {
         const char *s = StrVec[k].c_str();
         UTF8String JobName;
         char w[500 + 1];
         int Pos = 0;
         Pos += ReadWord(s + Pos, w, " :", 500);
         ToUpper(w);
         UTF8String ExternGroup = w;
         Pos += ReadWord(s + Pos, w, " :", 500);
         if ( w[0] == ':' )
         {
            Pos += ReadWord(s + Pos, w, " :", 500);
            ToUpper(w);
            JobName = ExternGroup + h_g::JobNameDelimiter + w;
         }
         else
         {
            JobName = ExternGroup;
         }
         FInfo.ExternJobList.push_back(JobName);
         if ( k > 0 )
         {
            FInfo.ExternJobListStr = FInfo.ExternJobListStr + "\n";
         }
         FInfo.ExternJobListStr = FInfo.ExternJobListStr + JobName;
      }
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

void __fastcall HStageGroup::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

UTF8String __fastcall HStageGroup::GetCaption(void)
{
   return FInfo.Group;
}

int __fastcall HStageGroup::GetIconType(void)
{
   return 5;
}

void __fastcall HStageGroup::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapGroupBg1[0]->Width * ( FInfo.Enlarge + 3 );
   Height = h_g::BitmapGroupBg1[0]->Height;
}

UTF8String __fastcall HStageGroup::GetHint()
{
   char TmpStr[8192 + 1];
   snprintf(TmpStr, 8192, h_g::I18nString("h_stage:GROUP_HINT"), FInfo.Group.c_str());
   UTF8String Hint = TmpStr;

   if( FInfo.Description != "" )
   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            Hint += UTF8String(h_g::I18nString("h_stage:DESCRIPTION")) + Text[i].c_str();
         else
            Hint += UTF8String("\n              ") + Text[i].c_str();
      }
   }

   return Hint;
}

void __fastcall HStageGroup::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
      GetIcon()->SetColor(h_g::itRunning);
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
   GetIcon()->SetExternFlag(!FInfo.ExternJobList.empty());
}

UTF8String __fastcall HStageGroup::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageGroup::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageGroup::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);

   JobListInfo.Group = FInfo.Group;
   JobListInfo.Tag = FInfo.Tag;
   JobListInfo.ExternJobList = FInfo.ExternJobList;
}

void __fastcall HStageGroup::GetInfo(HInfo &Info)
{
   Info = FInfo;
}

bool __fastcall HStageGroup::CanRerun()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageGroup::CanRestart()
{
   return FInfo.Status == "SUCCESS";
}

bool __fastcall HStageGroup::CanChangeCondition()
{
   return true;
}

void __fastcall HStageGroup::GetJobCondition(bool &CondNot,
   UTF8String &ConditionFile, UTF8String &SpecialDate,
   UTF8String &LoopStartTime, UTF8String &LoopEndTime)
{
   CondNot = FInfo.CondNot;
   ConditionFile = FInfo.ConditionFile;
   SpecialDate = FInfo.SpecialDate;
   LoopStartTime = "";
   LoopEndTime = "";
}

void __fastcall HStageGroup::SetJobCondition(bool CondNot,
   const UTF8String &ConditionFile, const UTF8String &SpecialDate,
   const UTF8String &LoopStartTime, const UTF8String &LoopEndTime)
{
   FInfo.CondNot = CondNot;
   FInfo.ConditionFile = ConditionFile;
   FInfo.SpecialDate = SpecialDate;
}

// class HStageTime

HStage * __fastcall HStageTime::Clone(void)
{
   return new HStageTime;
}

UTF8String __fastcall HStageTime::GetType(void)
{
   return "TIME";
}

int __fastcall HStageTime::GetIconEnlarge(void)
{
   return 0;
}

bool __fastcall HStageTime::EditProperty(bool PartFlag)
{
   return TimeForm->Edit(this);
}

void __fastcall HStageTime::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      FInfo.Time = Node->GetPropertyUpper("TIME").c_str();
      FInfo.SpecialDate = Node->GetProperty("SPECIAL_DATE").c_str();
      FInfo.CondNot = Node->GetProperty("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetProperty("CONDITION_FILE").c_str();
      FInfo.Tag = Node->GetProperty("TAG").c_str();
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      FInfo.Time = Node->GetValueUpper("TIME").c_str();
      FInfo.SpecialDate = Node->GetValue("SPECIAL_DATE").c_str();
      FInfo.CondNot = Node->GetValue("COND_NOT") == "Y";
      FInfo.ConditionFile = Node->GetValue("CONDITION_FILE").c_str();
      FInfo.Tag = Node->GetValue("TAG").c_str();
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   FInfo.Description = Node->GetValues("DESCRIPTION").c_str();

   {
      FInfo.ExternJobListStr = "";
      vector<string> StrVec;
      Node->GetValues("EXTERN_JOB_LIST", StrVec);
      for ( int k = 0; k < StrVec.size(); k++ )
      {
         const char *s = StrVec[k].c_str();
         UTF8String JobName;
         char w[500 + 1];
         int Pos = 0;
         Pos += ReadWord(s + Pos, w, " :", 500);
         ToUpper(w);
         UTF8String ExternGroup = w;
         Pos += ReadWord(s + Pos, w, " :", 500);
         if ( w[0] == ':' )
         {
            Pos += ReadWord(s + Pos, w, " :", 500);
            ToUpper(w);
            JobName = ExternGroup + h_g::JobNameDelimiter + w;
         }
         else
         {
            JobName = ExternGroup;
         }
         FInfo.ExternJobList.push_back(JobName);
         if ( k > 0 )
         {
            FInfo.ExternJobListStr = FInfo.ExternJobListStr + "\n";
         }
         FInfo.ExternJobListStr = FInfo.ExternJobListStr + JobName;
      }
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

void __fastcall HStageTime::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

UTF8String __fastcall HStageTime::GetCaption(void)
{
   return FInfo.Time;
}

int __fastcall HStageTime::GetIconType(void)
{
   return 6;
}

void __fastcall HStageTime::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapTimeBg[0]->Width;
   Height = h_g::BitmapTimeBg[0]->Height;
}

UTF8String __fastcall HStageTime::GetHint()
{
   return "";
}

void __fastcall HStageTime::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
      GetIcon()->SetColor(h_g::itRunning);
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
   GetIcon()->SetPartFlag(false);
   GetIcon()->SetExternFlag(!FInfo.ExternJobList.empty());
}

UTF8String __fastcall HStageTime::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageTime::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageTime::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);
   JobListInfo.Command = UTF8String("simbat_evnt wait '' '' '") +
      FInfo.Time + "' ''";
   JobListInfo.CommandUpper = JobListInfo.Command.UpperCase();
   JobListInfo.Tag = FInfo.Tag;

   JobListInfo.Status = FInfo.Status;
   JobListInfo.StartTime = FInfo.StartTime;
   JobListInfo.EndTime = FInfo.EndTime;
   JobListInfo.ElapsedTime = DiffTime(FInfo.StartTime, FInfo.EndTime, FInfo.IdleTimeSum);
   JobListInfo.IdleTimeSum = FInfo.IdleTimeSum;
   JobListInfo.TotalElapsed = JobListInfo.ElapsedTime;
}

void __fastcall HStageTime::GetPartTimeInfo(vector<HPartTimeInfo> &PartVec)
{
   UTF8String CurrentTime =
      TDateTime::CurrentDateTime().FormatString("yyyymmddhhnnss");
   UTF8String JobName = GetGroupId() + h_g::JobNameDelimiter + GetName();

   if ( FInfo.StartTime == "" ||
        ( FInfo.EndTime == "" && FInfo.Status != "RUNNING" ) )
   {
      return;
   }

   HPartTimeInfo Info;
   Info.PartName = JobName;
   Info.StartTime = FInfo.StartTime;
   Info.EndTime = FInfo.EndTime == "" ? CurrentTime : FInfo.EndTime;
   PartVec.push_back(Info);
}

UTF8String __fastcall HStageTime::GetRestoreStatus(void)
{
   return FInfo.RestoreStatus;
}

bool __fastcall HStageTime::CanSkip()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageTime::CanRestore()
{
   return false;
}

bool __fastcall HStageTime::CanRerun()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageTime::CanRestart()
{
   return FInfo.Status == "SUCCESS" || FInfo.Status == "SKIPPED";
}

bool __fastcall HStageTime::CanStop()
{
   return FInfo.Status == "RUNNING";
}

bool __fastcall HStageTime::CanViewLog()
{
   return FInfo.StartTime != "";
}

bool __fastcall HStageTime::CanViewAdditionalLog()
{
   return false;
}

void __fastcall HStageTime::GetInfo(HInfo &Info)
{
   FInfo.Name = GetName();
   Info = FInfo;
}

bool __fastcall HStageTime::CanChangeCondition()
{
   return true;
}

void __fastcall HStageTime::GetJobCondition(bool &CondNot,
   UTF8String &ConditionFile, UTF8String &SpecialDate,
   UTF8String &LoopStartTime, UTF8String &LoopEndTime)
{
   CondNot = FInfo.CondNot;
   ConditionFile = FInfo.ConditionFile;
   SpecialDate = FInfo.SpecialDate;
   LoopStartTime = "";
   LoopEndTime = "";
}

void __fastcall HStageTime::SetJobCondition(bool CondNot,
   const UTF8String &ConditionFile, const UTF8String &SpecialDate,
   const UTF8String &LoopStartTime, const UTF8String &LoopEndTime)
{
   FInfo.CondNot = CondNot;
   FInfo.ConditionFile = ConditionFile;
   FInfo.SpecialDate = SpecialDate;
}

// class HStageBatch

HStage * __fastcall HStageBatch::Clone(void)
{
   return new HStageBatch;
}

UTF8String __fastcall HStageBatch::GetType(void)
{
   return "BATCH";
}

int __fastcall HStageBatch::GetIconEnlarge(void)
{
   return FInfo.Enlarge;
}

bool __fastcall HStageBatch::EditProperty(bool PartFlag)
{
#ifdef SIMBAT_ENTERPRISE
   return BatchJobForm->Edit(this);
#else
   h_g::ShowMessage(UTF8String(h_g::I18nString("h_g:ENTERPRISE_ONLY")));
   return false;
#endif
}

void __fastcall HStageBatch::LoadProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
#ifdef SIMBAT_ENTERPRISE
   LoadProperty_aux(Node, XmlPropertyFlag);
#endif
}

void __fastcall HStageBatch::RefreshProperty(HXmlTree *Node, bool XmlPropertyFlag)
{
   HString StatusStr;

   if ( XmlPropertyFlag )
   {
      StatusStr = Node->GetPropertyUpper("STATUS");
      FInfo.RestoreStatus = Node->GetPropertyUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetProperty("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetProperty("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetProperty("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetProperty("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetProperty("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetProperty("PID").c_str());
   }
   else
   {
      StatusStr = Node->GetValueUpper("STATUS");
      FInfo.RestoreStatus = Node->GetValueUpper("RESTORE_STATUS").c_str();
      FInfo.StartTime = Node->GetValue("BEGINNING_TIME").c_str();
      if ( FInfo.StartTime == "" )
      {
         FInfo.StartTime = Node->GetValue("START_TIME").c_str();
      }
      FInfo.EndTime = Node->GetValue("END_TIME").c_str();
      FInfo.IdleTimeSum = StrToInt(Node->GetValue("IDLE_TIME_SUM").c_str());
      FInfo.ServerId = StrToInt(Node->GetValue("SERVER_ID").c_str());
      FInfo.Pid = StrToInt(Node->GetValue("PID").c_str());
   }

   {
      const char *s = StatusStr.c_str();
      if ( isdigit(*s) )
      {
         FInfo.Status = JobStatusToString(StrToInt(s));
      }
      else
      {
         FInfo.Status = s;
      }
   }
}

UTF8String __fastcall HStageBatch::GetCaption(void)
{
   return FInfo.Caption;
}

int __fastcall HStageBatch::GetIconType(void)
{
   return 7;
}

void __fastcall HStageBatch::GetIconSize(int &Width, int &Height)
{
   Width = h_g::BitmapBatchBg1[0]->Width * ( FInfo.Enlarge + 3 );
   Height = h_g::BitmapBatchBg1[0]->Height;
}

UTF8String __fastcall HStageBatch::GetHint()
{
   char TmpStr[8192 + 1];

   snprintf(TmpStr, 8192, h_g::I18nString("h_stage:BATCH_HINT"),
      GetName().c_str(), FInfo.HostName.c_str(), FInfo.Batch.c_str(), FInfo.Tag.c_str());
   UTF8String Hint = TmpStr;

   if( FInfo.Description != "" )
   {
      vector<string> Text;
      h_base::SplitString(FInfo.Description.c_str(), '\n', Text);
      for( int i = 0; i < Text.size(); i++ )
      {
         if( i == 0 )
            Hint += UTF8String(h_g::I18nString("h_stage:DESCRIPTION")) + Text[i].c_str();
         else
            Hint += UTF8String("\n              ") + Text[i].c_str();
      }
   }

   return Hint;
}

void __fastcall HStageBatch::SetColor(void)
{
   if( FInfo.Status == "WAITING" )
      GetIcon()->SetColor(h_g::itNormal);
   else if( FInfo.Status == "RUNNING" )
   {
      h_g::HIconType Color = h_g::itRunning;
      if ( FInfo.BatchIcon != NULL )
      {
         HBatchIcon *Batch = FInfo.BatchIcon;
         Batch->Lock();
         HOutlineStatus OutlineStatus = Batch->OutlineStatus;
         Batch->Unlock();
         if ( OutlineStatus.Status == h_g::itFailed ||
              OutlineStatus.Status == h_g::itUnknown ||
              OutlineStatus.Status == h_g::itPaused ||
              OutlineStatus.Status == h_g::itAlarm )
         {
            Color = h_g::itFailed;
         }
         GetIcon()->SetColor(Color);
      }
   }
   else if( FInfo.Status == "FAIL" )
      GetIcon()->SetColor(h_g::itFailed);
   else if( FInfo.Status == "SUCCESS" )
      GetIcon()->SetColor(h_g::itSuccess);
   else if( FInfo.Status == "SKIPPED" )
      GetIcon()->SetColor(h_g::itSkipped);
   else if( FInfo.Status == "UNKNOWN" )
      GetIcon()->SetColor(h_g::itUnknown);
   GetIcon()->SetPartFlag(false);
   GetIcon()->SetExternFlag(!FInfo.ExternJobList.empty());
}

UTF8String __fastcall HStageBatch::GetStatus(void)
{
   return FInfo.Status;
}

void __fastcall HStageBatch::SetStatus(const UTF8String &Status)
{
   FInfo.Status = Status;
   SetColor();
}

void __fastcall HStageBatch::GetJobListInfo(HJobListInfo &JobListInfo)
{
   HStage::GetJobListInfo(JobListInfo);
   JobListInfo.Command = UTF8String("");
   JobListInfo.CommandUpper = JobListInfo.Command.UpperCase();
   JobListInfo.Tag = FInfo.Tag;

   JobListInfo.Status = FInfo.Status;
   JobListInfo.StartTime = FInfo.StartTime;
   JobListInfo.EndTime = FInfo.EndTime;
   JobListInfo.ElapsedTime = DiffTime(FInfo.StartTime, FInfo.EndTime, FInfo.IdleTimeSum);
   JobListInfo.IdleTimeSum = FInfo.IdleTimeSum;
   JobListInfo.TotalElapsed = JobListInfo.ElapsedTime;
}

void __fastcall HStageBatch::GetPartTimeInfo(vector<HPartTimeInfo> &PartVec)
{
   UTF8String CurrentTime =
      TDateTime::CurrentDateTime().FormatString("yyyymmddhhnnss");
   UTF8String JobName = GetGroupId() + h_g::JobNameDelimiter + GetName();

   if ( FInfo.StartTime == "" ||
        ( FInfo.EndTime == "" && FInfo.Status != "RUNNING" ) )
   {
      return;
   }

   HPartTimeInfo Info;
   Info.PartName = JobName;
   Info.StartTime = FInfo.StartTime;
   Info.EndTime = FInfo.EndTime == "" ? CurrentTime : FInfo.EndTime;
   PartVec.push_back(Info);
}

UTF8String __fastcall HStageBatch::GetRestoreStatus(void)
{
   return FInfo.RestoreStatus;
}

bool __fastcall HStageBatch::CanSkip()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageBatch::CanRestore()
{
   return false;
}

bool __fastcall HStageBatch::CanRerun()
{
   return FInfo.Status == "FAIL" || FInfo.Status == "UNKNOWN";
}

bool __fastcall HStageBatch::CanRestart()
{
   return FInfo.Status == "SUCCESS" || FInfo.Status == "SKIPPED";
}

bool __fastcall HStageBatch::CanStop()
{
   return FInfo.Status == "RUNNING";
}

bool __fastcall HStageBatch::CanViewLog()
{
   return FInfo.StartTime != "";
}

bool __fastcall HStageBatch::CanViewAdditionalLog()
{
   return false;
}

void __fastcall HStageBatch::GetInfo(HInfo &Info)
{
   FInfo.Name = GetName();
   Info = FInfo;
}

bool __fastcall HStageBatch::CanChangeCondition()
{
   return true;
}

void __fastcall HStageBatch::GetJobCondition(bool &CondNot,
   UTF8String &ConditionFile, UTF8String &SpecialDate,
   UTF8String &LoopStartTime, UTF8String &LoopEndTime)
{
   CondNot = FInfo.CondNot;
   ConditionFile = FInfo.ConditionFile;
   SpecialDate = FInfo.SpecialDate;
   LoopStartTime = "";
   LoopEndTime = "";
}

void __fastcall HStageBatch::SetJobCondition(bool CondNot,
   const UTF8String &ConditionFile, const UTF8String &SpecialDate,
   const UTF8String &LoopStartTime, const UTF8String &LoopEndTime)
{
   FInfo.CondNot = CondNot;
   FInfo.ConditionFile = ConditionFile;
   FInfo.SpecialDate = SpecialDate;
}

bool __fastcall HStageBatch::MatchServerIdx(int ServerIdx)
{
   HBatchIcon *BatchIcon = FInfo.BatchIcon;

   if ( BatchIcon == NULL )
   {
      return false;
   }

   return BatchIcon->Idx == ServerIdx;
}

#pragma package(smart_init)
